package yang.VirtualMachine.chapter11.javaagent;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

import yang.VirtualMachine.chapter11.asmTime.Account;
import yang.VirtualMachine.chapter11.asmTime.TimeStatClassAdater;

public class PreMainTraceAgent {
	
	public static void agentmain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException {
		System.out.println("agentmain called");
		System.out.println("agentmain agentArgs:" + agentArgs);
		premain(agentArgs, inst);
		// 此行代码会导致启动时执行premain中不会拦截到Account
		// 即premain方法只有在启动后重加载时才会加载到Account
		inst.retransformClasses(Account.class);
	}
	
	public static void premain(String agentArgs, Instrumentation inst) {
		System.out.println("premain agentArgs:" + agentArgs);
		// 增加一个Class文件转换器，用于改变Class二进制流
		// 参数canRetransform设置是否允许重新转换,若不允许则会导致attach不生效
		inst.addTransformer(new ClassFileTransformer() {
			
			/* (non-Javadoc)
			 * @see java.lang.instrument.ClassFileTransformer#transform(java.lang.ClassLoader, java.lang.String, java.lang.Class, java.security.ProtectionDomain, byte[])
			 */
			@Override
			public byte[] transform(ClassLoader loader, // 定义类的加载器
					String className, // 类的全限定名，例如java/lang/String
					Class<?> classBeingRedefined, // 如果是被重新定义或重新转换触发，则为重定义或重转换的类，类加载则为null
					ProtectionDomain protectionDomain, // 要定义或重定义的类的保护域
					byte[] classfileBuffer		// 表示类的2进制，只读，不可修改
					) throws IllegalClassFormatException {
				System.out.println("load Class:" + className);
				
				// 不可使用Account.class.getName()方法，否则在加载此类的时候就已经加载了Account
				// 导致此方法无法拦截器加载
//				if (className.equals(Account.class.getName().replace(".", "/"))) {
				if (className.equals("yang.VirtualMachine.chapter11.asmTime.Account".replace(".", "/"))) {
					ClassReader cr = new ClassReader(classfileBuffer);
					ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
					TimeStatClassAdater classAdapter = new TimeStatClassAdater(cw);
					cr.accept(classAdapter, ClassReader.SKIP_DEBUG);
					return cw.toByteArray();
				}
				return classfileBuffer;
			}
		}, true);
	}
}
